.set				noreorder

.equ				RDRAM_START, 0x80000000
.equ				TRAINER_OFFSET,	0x00400000

;ROM
;.org				0xB1FFF000
;RAM
;.org				0x80001000 + TRAINER_OFFSET

;-------------------------------;
;	BEGIN CHEATS		;
;-------------------------------;

;	Rainbow Blood
.long				0x800A5080
.long				0x00000001
;	End Rainbow Blood

;-------------------------------;

;	Co-Op Hack Variables
.equ				gameActive, 0x00011A9C
;	2 bytes
;	false (anything but 0x0001) means in a menu or similar
;	true (0x0001) means actually playing
.equ				tribalDataLoadState, 0x00011A9E
;	2 bytes
;	0 - Not ready (waiting for warpingDone && no buttons pressed)
;	1 - Waiting (for L, and only L)
;	2 - Active (load tribal data while region matches)
;	3 - Done (region mismatch; quit)
.equ				lastLocation, 0x00011AA0
;	2 bytes
.equ				lastRegion, 0x00011AA2
;	1 byte
.equ				lastPlanet, 0x00011AA3
;	1 byte
.equ				saveID, 0x00011AA4
;	4 bytes
;	Currently not updated and can only be the first, save 0
.equ				playerPointers, 0x00011AA8
;	0x10 bytes
;	The array of pointers to players' characters' data in co-op
.equ				menuHackVars, 0x00011AB8
;	0x08 bytes
;	These bytes are for variables that aren't related to tribal data
.equ				lastKeys, 0x00011ABC
;	2 bytes
;	Bits of which keys were pressed by player 1 during the previous execution
.equ				LnewlyPressed, 0x00011ABE
;	2 bytes
;	0 for false, anything else for true
.equ				warpingDone, 0x00011AC0
;	4 bytes
;	0xDEADBEEF for true, anything else for false
.equ				saveLoaded, 0x00011AC4
;	4 bytes
;	-1 means false; 0 means loaded but not copied to the correct area"
.equ				saveCache, 0x00011AC8
;	SAVE_LENGTH bytes (see below)

;Within the save:

.equ				SAVE_LENGTH, 0x538

.equ				PLAYER_4_DATA, 0x0E6

.equ				VELA_LEVELS, 0x0500
.equ				JUNO_LEVELS, 0x0502
.equ				LUPUS_LEVELS, 0x0504

.equ				TRIBAL_CACHE, 0x0508
;(Format is like lastLocation to saveID as seen in trainer globals listed above; thus, 0x4 bytes)
;	End of Co-Op Hack Variables

;-------------------------------;

;	Co-Op Save Hack
.long				0xC0DE0000
.long				0x00000000

;-------------------------------;
;	Co-Op Save Hack		;
;-------------------------------;

;	Ensure we are in multiplayer mode
LUI	S1,	0x800A		;
LB	S2,	0x5060	(S1)	;Check mode
BEQZ	S2,			Reset_Data
NOP

ORI	S1,	R0,	0x0001	;Quit if not in multiplayer
BNE	S1,	S2,		Co_Op_Save_Hack_End
NOP

BEQZ	R0,			COSH_In_Multiplayer
NOP

				Reset_Data:
ADDIU	S2,	R0,	-0x0001	;
LUI	S1,	%hi(saveID + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveID + RDRAM_START + TRAINER_OFFSET)
SW	S2,	0x0000	(S1)	;
LUI	S1,	%hi(warpingDone + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)
SW	S2,	0x0000	(S1)	;
SW	S2,	0x0004	(S1)	;Reset "saveID", "warpingDone" and "saveLoaded" variables
				;if single player mode is active
BEQZ	R0,			Co_Op_Save_Hack_End
NOP

				COSH_In_Multiplayer:
;	Check if we loaded the save already
LUI	S1,	%hi(saveLoaded + RDRAM_START + TRAINER_OFFSET)
				;Only load save if it hasn't been loaded
				;This read's purpose is explained by
				;a write to this address which occurs later in the code
LW	S1,	%lo(saveLoaded + RDRAM_START + TRAINER_OFFSET)	(S1)
				;[saveLoaded] >= 0?
BGEZ	S1,			No_Load
NOP

LUI	S1,	0x8010		;
LH	S2,	0x5304	(S1)	;
XORI	S2,	S2,	0x0020	;Check L button; load if only L pressed
BNEZ	S2,			No_Load
NOP

;	Get ID of save to use
LUI	S1,	%hi(saveID + RDRAM_START + TRAINER_OFFSET)
LW	S1,	%lo(saveID + RDRAM_START + TRAINER_OFFSET)	(S1)
				;ID of the save to load,
				;from 1 through 6, to be initalized elsewhere
ADDIU	S1,	S1,	-0x0001	;Subtract 1; ID is now 0 through 5
BLTZL	S1,			Save_ID
OR	S1,	R0,	R0	;Save 0 by default
ADDIU	S2,	S1,	0x0005	;Bounds checking
BGTZL	S2,			Save_ID
OR	S1,	R0,	R0	;Save 0 by default

				Save_ID:
ORI	S2,	R0,	0x000E	;Shifting left by 0xE multiplies by 0x4000,
				;the size of saves on the save media
SLLV	S5,	S1,	S2	;S5 = flash offset for selected save

LUI	S0,	0xA460		;0xA4600000
LUI	S1,	%hi(saveCache + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveCache + TRAINER_OFFSET)
				;Where the save will be loaded to
LUI	S2,	0x0800		;0x08000000; Flash base address
				;(other saves at save# * 0x4000; 6 in all starting with #0)
ADDU	S2,	S2,	S5	;Add the offset for the selected save
ORI	S3,	R0,	SAVE_LENGTH - 1
;ORI	S4,	R0,	0x0002	;0x00000002; this does not need to be written
				;to 0xA4600010 like usual because the exception
				;handler the game initalizes will do this anyway
				;(incidentally this is why this code causes
				;a recursion crash without the semaphore
				;implemented above)

				Wait_Loop:
LW	S5,	0x0010	(S0)	;
ANDI	S5,	S5,	0x0003	;
BNEZ	S5,			Wait_Loop
NOP

LUI	S4,	%hi(saveLoaded + RDRAM_START + TRAINER_OFFSET)
				;This will prevent the exception handler
				;from calling this code again recursively
				;(this code is executed in the exception handler
				;which is triggered when DMA is done,
				;such as by writing to 0xA460000C like in this case)
				;based on a check performed earlier in this code
SW	R0,	%lo(saveLoaded + RDRAM_START + TRAINER_OFFSET)	(S4)
				;[saveLoaded] = 0 (-1 means false; 0 means
				;"loaded but not copied to the correct area")
BEQZ	R0,			Co_Op_DMA_Write
NOP
.long				0xDEADC0DE
				Co_Op_DMA_Write:
SW	S1,	0x0000	(S0)	;RAM
SW	S2,	0x0004	(S0)	;Hardware
SW	S3,	0x000C	(S0)	;Size

;	Load tribal data from cache in save to cache in globals
LUI	S1,	%hi(lastLocation + RDRAM_START + TRAINER_OFFSET)
ORI	S2,	S1,	%lo(lastLocation + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)
ORI	S3,	R0,	0x0004	;Number of bytes to copy
				;S1 points to cached save
				;S2 points to global tribal data cache
SH	R0,	-0x0002	(S2)	;Clear tribalDataLoadState
				Tribal_Data_Load_Loop:
ADDIU	S3,	S3,	-0x0002	;
LHU	S4,	TRIBAL_CACHE	(S1)
ADDIU	S1,	S1,	0x0002	;
SH	S4,	0x0000	(S2)	;
BNEZ	S3,			Tribal_Data_Load_Loop
ADDIU	S2,	S2,	0x0002	;This loop is overkill at the moment but the decided size of
				;TRIBAL_CACHE changed rapidly during debugging

;	Handle characters
LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)
				;Save cache
ORI	S2,	R0,	0x0007	;Lupus, Juno and Vela (bits 4, 2 and 1 respectively)
LBU	S3,	0x0030	(S1)	;
OR	S2,	S2,	S3	;
SB	S2,	0x0030	(S1)	;Set all characters unlocked

;	Cache levels within the cache
LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)
LHU	S2,	0x01C0	(S1)	;
SH	S2,	VELA_LEVELS	(S1)	;Vela
LHU	S2,	0x0236	(S1)	;
SH	S2,	JUNO_LEVELS	(S1)	;Juno
LHU	S2,	0x02AC	(S1)	;
SH	S2,	LUPUS_LEVELS	(S1)	;Lupus

;	Copy relevant data from save cache to multiplayer data
LUI	S2,	%hi(lastKeys + RDRAM_START + TRAINER_OFFSET)
ORI	S3,	R0,	0x0020	;"L pressed"
SH	S3,	%lo(lastKeys + RDRAM_START + TRAINER_OFFSET)	(S2)
				;Written to last_keys global

LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)
ORI	S2,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)
				;Save cache
ADDIU	S2,	S2,	0x015C	;Player 1 data
LUI	S3,	0x800F		;
ORI	S3,	S3,	0xED66	;0x800FED66 - Multiplayer data
ORI	S4,	R0,	0x0076	;Single player data length
ORI	S5,	R0,	0x002A	;Multiplayer data length - single player data length
ORI	S6,	R0,	0x0003	;Do this for the first 3 players

				Player_Load:
LH	S7,	0x0000	(S2)	;
SH	S7,	0x0000	(S3)	;
ADDIU	S2,	S2,	0x0002	;
ADDIU	S3,	S3,	0x0002	;
ADDIU	S4,	S4,	-0x0002	;
BGTZ	S4,			Player_Load
NOP
ADDIU	S6,	S6,	-0x0001	;

ORI	S4,	R0,	0x0076	;
ADDU	S3,	S3,	S5	;
BLTZ	S6,			Players_Loaded
NOP
BGTZ	S6,			Player_Load
NOP

ORI	S2,	S1,	%lo(saveCache + PLAYER_4_DATA + RDRAM_START + TRAINER_OFFSET)
BEQZ	R0,			Player_Load
NOP

				Players_Loaded:
;	Set that the save was loaded
ORI	S2,	R0,	0x0001	;
LUI	S1,	%hi(saveLoaded + RDRAM_START + TRAINER_OFFSET)
SW	S2,	%lo(saveLoaded + RDRAM_START + TRAINER_OFFSET)	(S1)

;	Copy loaded save from save cache to 0x801E6010
;	(the RAM address saves are saved from and loaded to normally)
LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)
LUI	S2,	0x801E		;
ORI	S2,	S2,	0x6010	;
ADDIU	S3,	S1,	SAVE_LENGTH

;S1 = src
;S2 = dest
;S3 = should point to after end of src data
				To_Loop:
LW	S0,	0x0000	(S1)	;
SW	S0,	0x0000	(S2)	;
ADDIU	S1,	S1,	0x0004	;
ADDIU	S2,	S2,	0x0004	;

BEQ	S1,	S3,		Mem_Cpy_1_Done
NOP

BEQZ	R0,			To_Loop
NOP

				Mem_Cpy_1_Done:
;	We don't need this here but it would be inefficient
;	to copy the data back as soon as we load it,
;	especially since we've spent plenty of time sitting in the
;	exception handler as it is
BEQZ	R0,			Co_Op_Save_Hack_End
NOP

				No_Load:
;	Check if save has been loaded (to multiplayer)
;	and if it hasn't, exit
LUI	S1,	%hi(saveLoaded + RDRAM_START + TRAINER_OFFSET)
LW	S1,	%lo(saveLoaded + RDRAM_START + TRAINER_OFFSET)	(S1)
ADDIU	S1,	S1,	-0x0001	;
BNEZ	S1,			Co_Op_Save_Hack_End
NOP

;	OR keys/objects together for all players and store them
LUI	S1,	0x800F		;
ORI	S1,	S1,	0xED66	;Player data array pointer
				;Keys/objects are at +0x66 in the player data
				;Player data is 0xA0 in size
OR	S2,	R0,	R0	;Keys
OR	S3,	R0,	R0	;Objects
ORI	S5,	R0,	0x0004	;4 players

				Keys_Objects_Combine:
LHU	S4,	0x0066	(S1)	;
OR	S2,	S2,	S4	;
LHU	S4,	0x0068	(S1)	;
OR	S3,	S3,	S4	;
ADDIU	S1,	S1,	0x00A0	;
ADDIU	S5,	S5,	-0x0001	;
BNEZ	S5,			Keys_Objects_Combine
NOP

ORI	S5,	R0,	0x0004	;

				Update_Keys_Objects:
ADDIU	S1,	S1,	-0x00A0	;
SH	S2,	0x0066	(S1)	;
SH	S3,	0x0068	(S1)	;
ADDIU	S5,	S5,	-0x0001	;
BNEZ	S5,			Update_Keys_Objects
NOP

				Copy_To_Save_RAM:
;	Copy relevant data from multiplayer data to save data
LUI	S1,	0x801E		;
ORI	S2,	S1,	0x6010	;
				;Save address
ADDIU	S2,	S2,	0x015C	;Player 1 data
LUI	S3,	0x800F		;
ORI	S3,	S3,	0xED66	;0x800FED66 - Multiplayer data
ORI	S4,	R0,	0x0076	;Single player data length
ORI	S5,	R0,	0x002A	;Multiplayer data length - single player data length
ORI	S6,	R0,	0x0003	;Do this for the first 3 players

				Player_Store:
LH	S7,	0x0000	(S3)	;
SH	S7,	0x0000	(S2)	;
ADDIU	S2,	S2,	0x0002	;
ADDIU	S3,	S3,	0x0002	;
ADDIU	S4,	S4,	-0x0002	;
BGTZ	S4,			Player_Store
NOP
ADDIU	S6,	S6,	-0x0001	;

ORI	S4,	R0,	0x0076	;
ADDU	S3,	S3,	S5	;
BLTZ	S6,			Players_Stored
NOP
BGTZ	S6,			Player_Store
NOP

ORI	S2,	S1,	PLAYER_4_DATA + 0x6010
BEQZ	R0,			Player_Store
NOP

				Players_Stored:
;	Calculate unlocked levels in the save cache's
;	unlocked levels backups (stored at +VELA_LEVELS)
;	to the usual unlocked levels variables in the save
LUI	S0,	0x801E		;
ORI	S0,	S0,	0x6010	;
				;Save address
LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)		;
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)	;

ORI	S2,	R0,	0x7FFB	;All levels but
				;Mizar's Palace (0x0004)
				;(top bit is irrelevant)
OR	S4,	R0,	R0	;

LHU	S3,	VELA_LEVELS	(S1)	;Vela's levels
AND	S3,	S3,	S2	;
OR	S4,	S4,	S3	;

LHU	S3,	JUNO_LEVELS	(S1)	;Juno's levels
AND	S3,	S3,	S2	;
OR	S4,	S4,	S3	;

LHU	S3,	LUPUS_LEVELS	(S1)	;Lupus's levels
AND	S3,	S3,	S2	;
OR	S4,	S4,	S3	;

ORI	S4,	S4,	0x4740	;Walkway, Goldwood, Spawnship,
				;SS Anubis and Sekhmet unlocked by default

LUI	S2,	0x800A		;
LHU	S2,	0x3250	(S2)	;S2 == Place we're at

;	Open the level after the one we're at the exit of

ORI	S5,	R0,	0x000E	;End of Cerulean
BEQL	S2,	S5,		Unlock_Ichor
ORI	S4,	S4,	0x0008	;Open Ichor
				Unlock_Ichor:

ORI	S5,	R0,	0x0011	;End of Goldwood (Lodge)
BEQL	S2,	S5,		Unlock_Space_Station
ORI	S4,	S4,	0x1000	;Open Space Station
				Unlock_Space_Station:

ORI	S5,	R0,	0x002E	;End of Sekhmet
BEQL	S2,	S5,		Unlock_Cerulean
ORI	S4,	S4,	0x0080	;Open Cerulean
				Unlock_Cerulean:

ORI	S5,	R0,	0x0034	;Second end of Sekhmet
BEQL	S2,	S5,		Unlock_Water_Ruin
ORI	S4,	S4,	0x2000	;Open Water Ruin
				Unlock_Water_Ruin:

ORI	S5,	R0,	0x004C	;End of Spawnship
BEQL	S2,	S5,		Unlock_Rith_Essa
ORI	S4,	S4,	0x0001	;Open Rith Essa
				Unlock_Rith_Essa:

ORI	S5,	R0,	0x0052	;End of Rith Essa
BEQL	S2,	S5,		Unlock_Eschebone
ORI	S4,	S4,	0x0010	;Open Eschebone
				Unlock_Eschebone:

ORI	S5,	R0,	0x0054	;End of SS Anubis
BEQL	S2,	S5,		Unlock_Tawfret
ORI	S4,	S4,	0x0020	;Open Tawfret
				Unlock_Tawfret:

ORI	S5,	R0,	0x00E4	;End of Goldwood (Rim)
BEQL	S2,	S5,		Unlock_Gem_Quarry
ORI	S4,	S4,	0x0800	;Open Gem Quarry
				Unlock_Gem_Quarry:

				Asteroid_Check:
;	Open Asteroid if we're in the ship room with all ship parts
ORI	S5,	R0,	0x0190	;
BNE	S2,	S5,		Level_Mix
NOP
LUI	S5,	0x801E		;
LHU	S5,	0x6044	(S5)	;S5 == Ship parts without mask
ORI	S6,	R0,	0xFF0F	;Ship part mask
AND	S5,	S5,	S6	;S5 == Ship parts with mask
BEQL	S5,	S6,		Level_Mix
ORI	S4,	S4,	0x0002	;Unlock Asteroid

				Level_Mix:
LHU	S3,	VELA_LEVELS	(S1)	;Cache of Vela's levels
OR	S3,	S3,	S4	;
;	If we're at the end of Ichor, OR bit for Mizar's Palace
ORI	S5,	R0,	0x0012	;
BEQL	S2,	S5,		Unlock_Vela_Entrance
ORI	S3,	S3,	0x0004	;
				Unlock_Vela_Entrance:
SH	S3,	VELA_LEVELS	(S1)	;Updated
SH	S3,	0x01C0	(S1)	;Vela
SH	S3,	VELA_LEVELS	(S0)	;Repeat for actual save
SH	S3,	0x01C0	(S0)	;

LHU	S3,	JUNO_LEVELS	(S1)	;Cache of Junos's levels
OR	S3,	S3,	S4	;
;	If we're at the end of Tawfret, OR bit for Mizar's Palace
ORI	S5,	R0,	0x0088	;
BEQL	S2,	S5,		Unlock_Juno_Entrance
ORI	S3,	S3,	0x0004	;
				Unlock_Juno_Entrance:
SH	S3,	JUNO_LEVELS	(S1)	;Updated
SH	S3,	0x0236	(S1)	;Juno
SH	S3,	JUNO_LEVELS	(S0)	;Repeat for actual save
SH	S3,	0x0236	(S0)	;

LHU	S3,	LUPUS_LEVELS	(S1)	;Cache of Lupus's levels
OR	S3,	S3,	S4	;
;	If we're at the end of Eschebone, OR bit for Mizar's Palace
ORI	S5,	R0,	0x011F	;
BEQL	S2,	S5,		Unlock_Lupus_Entrance
ORI	S3,	S3,	0x0004	;
				Unlock_Lupus_Entrance:
SH	S3,	LUPUS_LEVELS	(S1)	;Updated
SH	S3,	0x02AC	(S1)	;Lupus
SH	S3,	LUPUS_LEVELS	(S0)	;Repeat for actual save
SH	S3,	0x02AC	(S0)	;

;	Copy loaded save from 0x801E6010 to save cache
LUI	S1,	%hi(saveCache + RDRAM_START + TRAINER_OFFSET)		;
ORI	S1,	S1,	%lo(saveCache + RDRAM_START + TRAINER_OFFSET)	;
LUI	S2,	0x801E		;
ORI	S2,	S2,	0x6010	;
ADDIU	S3,	S1,	SAVE_LENGTH

;S1 = src
;S2 = dest
;S3 = should point to after end of src data
				From_Loop:
LW	S0,	0x0000	(S2)	;
SW	S0,	0x0000	(S1)	;
ADDIU	S1,	S1,	0x0004	;
ADDIU	S2,	S2,	0x0004	;

BEQ	S1,	S3,		Mem_Cpy_2_Done
NOP

BEQZ	R0,			From_Loop
NOP

				Mem_Cpy_2_Done:
				Co_Op_Save_Hack_End:
JR	RA
NOP

.align				3
.long				0xE0000000
.long				0x00000000
;	End of Co-Op Save Hack

;-------------------------------;

;	Menu Hack
;	If co-op data has been loaded in multiplayer mode and the team hasn't warped,
;	enable warping to Gem Quarry (warping to Gem Quarry doesn't work; actually using
;	location 0x0010 instead) via L button
;	(If warping is done, set warpingDone + RDRAM_START + TRAINER_OFFSET (aka warpingDone)
;	to 0xDEADBEEF)
;	Begin C0DE segment
.long				0xC0DE0000
.long				0x00000000

LUI	S0,	0x800A		;
LBU	S0,	0x5060	(S0)	;S0 = menu mode byte
ORI	S1,	R0,	0x0001	;S1 = multiplayer mode value
LUI	S2,	0x800A		;S2 = 0x800A0000
BNE	S0,	S1,		Menu_Hack_Exit
NOP				;Don't do anything if not in multiplayer mode
LBU	S3,	0x4FD0	(S2)	;S3 = number of players
SB	S3,	0x325D	(S2)	;Force split screen in multi

LUI	S4,	%hi(saveLoaded + RDRAM_START + TRAINER_OFFSET)
LW	S5,	%lo(saveLoaded + RDRAM_START + TRAINER_OFFSET)	(S4)
				;Don't do anything if Co-Op data hasn't been loaded
ADDIU	S5,	S5,	-0x0001	;
BNEZ	S5,			Menu_Hack_Exit
LW	S5,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)	(S4)
LUI	S6,		0xDEAD	;
ORI	S6,	S6,	0xBEEF	;Don't do anything if warping has been done
BEQ	S5,	S6,		Menu_Hack_Exit

LUI	S0,	0x8010		;
LH	S0,	0x5304	(S0)	;
ANDI	S0,	S0,	0x0020	;L button status in S0

LUI	S1,	%hi(lastKeys + RDRAM_START + TRAINER_OFFSET)
LH	S2,	%lo(lastKeys + RDRAM_START + TRAINER_OFFSET)	(S1)
				;Previous L button status in S2
SH	S0,	%lo(lastKeys + RDRAM_START + TRAINER_OFFSET)	(S1)
				;Update previous status

XORI	S2,	S2,	0x0020	;last_keys = ~last_keys
AND	S0,	S0,	S2	;keys_down = ~last_keys & keys

ORI	S3,	R0,	0x0001	;S3 = true

;	L newly pressed set to true
BEQZ	S0,			Menu_Hack_Exit
NOP
SH	S3,	%lo(LnewlyPressed + RDRAM_START + TRAINER_OFFSET)	(S1)
LUI	S3,		0xDEAD	;
ORI	S3,	S3,	0xBEEF	;
SW	S3,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)	(S1)	;
				;Kill this code before it crashes the game

				Menu_Hack_Exit:
JR	RA			;
NOP

.align				3
.long				0xE0000000
.long				0x00000000
;	End C0DE segment

;	"If L newly pressed" part of code
.long				0xD1000000 + LnewlyPressed + TRAINER_OFFSET
.long				0x00000001
;	gameActive?
.long				0xD1000000 + gameActive + TRAINER_OFFSET
.long				0x00000001
;	ERROR: This writes 0xFF, or at least the 0x01 becomes 0xFF;
;	it must be changed back to 0x01 once (and only once) to keep the emulator
;	from crashing
.long				0x800A3294
.long				0x00000001
;	FIXME: 0x0110 (Gem Quarry) is more aesthetic but doesn't work,
;	but 0x0010 (cave on Goldwood) is an ugly starting point (although it does work)
;	NOTE: 0x0010 doesn't work very often in PJ 64, but 0x0016 (Walkway) and 0x0110 do
;	NOTE: 0x0016 works in Mupen as well, so switching to that
.long				0x810A3250
.long				0x00000016
;	Tell other code to fix bug
.long				0x82000000 + menuHackVars + TRAINER_OFFSET
.long				0xDEADBEEF
;	Set L as not pressed
.long				0x81000000 + LnewlyPressed + TRAINER_OFFSET
.long				0x00000000
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000

;	Handle bug of previous code
.long				0xD2000000 + menuHackVars + TRAINER_OFFSET
.long				0xDEADBEEF
.long				0xD00A3294
.long				0x000000FF
.long				0x800A3294
.long				0x00000001
;	Have this code disable itself
.long				0x82000000 + menuHackVars + TRAINER_OFFSET
.long				0x00000000
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000

;	If 0x80046158 == (instruction that's usually there)
;	Do 0x87 code type for enabling single player menu if and only if
;	mode is single player OR co-op warping has been done
;	Also stores location/region/planet to save area
.long				0xD2046158
LBU	T9,	0x4FC4	(T9)	;Original code
.long				0x87046158
.long				0x00000000
;	T0 is free
ADDIU	SP,	SP,	-0x000C	;Stack stuff
SW	S0,	0x0000	(SP)	;""
SW	S1,	0x0004	(SP)	;""
SW	S2,	0x0008	(SP)	;""
LUI	T0,	%hi(0x800A323C)	;
LHU	T0,	%lo(0x800A323C)	(T0)	;
				;[location]
ORI	S2,	R0,	0x0118	;Size of room data at 0x00CB3100
MFHI	S0			;
MFLO	S1			;
MULTU	T0,	S2		;
MFLO	S2			;S2 holds offset
MTHI	S0			;
MTLO	S1			;
LUI	S1,	%hi(0x801E6010)	;Save address
ORI	S1,	S1,	%lo(0x801E6010)	;
SH	T0,	%lo(0x00 + TRIBAL_CACHE)	(S1)	;
				;lastLocation
LUI	T0,	%hi(0xB0CB3100)	;
ORI	T0,	T0,	%lo(0xB0CB3100)	;
ADDU	S0,	T0,	S2	;Using data from ROM instead of what's cached in RAM -
				;apparently what's in the RAM could be wrong (which might
				;be due to doing single player mode stuff in multi)
LBU	S2,	0x0020	(S0)	;
SB	S2,	%lo(0x03 + TRIBAL_CACHE)	(S1)	;
				;lastPlanet
LBU	S2,	0x0102	(S0)	;
SB	S2,	%lo(0x02 + TRIBAL_CACHE)	(S1)	;
				;lastRegion
;	Enable menu when appropriate
LBU	T9,	0x4FC4	(T9)	;Original code
BEQZ	T9,			Menu_Hack_Enable_Return
NOP
LUI	S0,	%hi(warpingDone + RDRAM_START + TRAINER_OFFSET)
LW	S0,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)	(S0)
LUI	S1,	0xDEAD		;
ORI	S1,	S1,	0xBEEF	;Warping done?
BEQL	S0,	S1,		MHE_Warping_Done
ORI	T9,	R0,	0x0000	;Single player menu enabled
				MHE_Warping_Done:
				Menu_Hack_Enable_Return:
LW	S0,	0x0000	(SP)	;Stack stuff
LW	S1,	0x0004	(SP)	;""
LW	S2,	0x0008	(SP)	;""
ADDIU	SP,	SP,	0x000C	;""
J				0x0004615C
NOP
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000
;	End of Menu Hack

;-------------------------------;

;	Position Sync Hack
;	If and only if warping is done, cause other players to spawn near player 1
;	when L is pressed and spawn them together when entering a new room

;	If 0x80011C4C == (instruction that's usually there)
;	Do 0x87 code type for enabling single player menu if and only if
;	mode is single player OR co-op warping has been done
.long				0xD2011C4C
ADDIU	V0,	V0,	0x0001	;Original code
.long				0x87011C4C
.long				0x00000000
;	AT, A0 and T7 are free
;	menuHackVars + RDRAM_START + TRAINER_OFFSET
;	has 16 bytes free contiguous with the rest of this hack
ADDIU	V0,	V0,	0x0001	;Original code

;	Quit if warping hasn't been done
LUI	AT,	%hi(warpingDone + RDRAM_START + TRAINER_OFFSET)
LW	AT,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)	(AT)
LUI	A0,	0xDEAD		;
ORI	A0,	A0,	0xBEEF	;Warping done?
BNE	AT,	A0,		Position_Sync_Hack_Return
NOP

ADDIU	SP,	SP,	-0x0018	;Stack stuff
SW	S0,	0x0000	(SP)	;""
SW	S1,	0x0004	(SP)	;""
SW	S2,	0x0008	(SP)	;""
SW	S3,	0x000C	(SP)	;""
SW	S4,	0x0010	(SP)	;""
SW	S5,	0x0014	(SP)	;""

LUI	S0,	%hi(playerPointers + RDRAM_START + TRAINER_OFFSET)
ORI	S0,	S0,	%lo(playerPointers + RDRAM_START + TRAINER_OFFSET)
				;Pointers will be stored at [S0]
LW	S1,	0x004C	(T6)	;S1 = Pointer to a player's character's data + 0x7F0
ADDIU	S2,	R0,	-0x0001	;S2 = Uninitialized pointer
LUI	S4,	0x800A		;
LB	S4,	0x325D	(S4)	;[0x800A325D] (Player count)
OR	S3,	S4,	R0	;S3 = S4 = Player count

				Dump_Loop:
LW	S5,	0x0000	(S0)	;S5 = [S0]
BEQL	S5,	S2,		Dump
SW	S1,	0x0000	(S0)	;Pointer dumped
BEQ	S5,	S1,		Already_Dumped
NOP
ADDIU	S0,	S0,	0x0004	;
ADDIU	S3,	S3,	-0x0001	;
BNEZ	S3,			Dump_Loop
NOP

;	Loop expired; we have new pointers
LUI	S0,	%hi(playerPointers + RDRAM_START + TRAINER_OFFSET)
ORI	S0,	S0,	%lo(playerPointers + RDRAM_START + TRAINER_OFFSET)
SW	S1,	0x0000	(S0)	;First player?
SW	S2,	0x0004	(S0)	;Clear other pointers
SW	S2,	0x0008	(S0)	;""
BEQZ	R0,			Dump_Done
SW	S2,	0x000C	(S0)	;""

				Dump:
ADDIU	S3,	S3,	-0x0001	;
BEQZ	S3,			Synchronize
NOP
BEQZ	R0,			Dump_Done
NOP

				Already_Dumped:
LUI	S1,	0x8010		;
LH	S2,	0x5304	(S1)	;
XORI	S2,	S2,	0x0020	;Check L button; sync if only L pressed
BNEZ	S2,			Dump_Done
NOP
				Synchronize:
LUI	S0,	%hi(playerPointers + RDRAM_START + TRAINER_OFFSET)
ORI	S0,	%lo(playerPointers + RDRAM_START + TRAINER_OFFSET)
				;S0 points to player character pointer array
LW	S1,	0x0000	(S0)	;S1 points to player 1's character
ADDIU	S1,	S1,	-0x07F0	;S1 points to the beginning of the data
				;of player 1's character
ADDIU	S3,	S4,	-0x0001	;S3 is the player count minus 1
				;(the number of players to sync to player 1)

				Sync_Loop:
LW	S2,	0x0004	(S0)	;S2 points to the next player's character
ADDIU	S2,	S2,	-0x07F0	;S2 points to the beginning of the data
				;of the next player's character
LW	S5,	0x000C	(S1)	;
SW	S5,	0x000C	(S2)	;X sync'd
LW	S5,	0x0010	(S1)	;
SW	S5,	0x0010	(S2)	;Z sync'd
LW	S5,	0x0014	(S1)	;
SW	S5,	0x0014	(S2)	;Y sync'd
LH	S5,	0x01D8	(S1)	;
SH	S5,	0x01D8	(S2)	;Rotation sync'd
ADDIU	S0,	S0,	0x0004	;
ADDIU	S3,	S3,	-0x0001	;
BNEZ	S3,			Sync_Loop
NOP

				Dump_Done:
LW	S0,	0x0000	(SP)	;Stack stuff
LW	S1,	0x0004	(SP)	;""
LW	S2,	0x0008	(SP)	;""
LW	S3,	0x000C	(SP)	;""
LW	S4,	0x0010	(SP)	;""
LW	S5,	0x0014	(SP)	;""
ADDIU	SP,	SP,	0x0018	;""

				Position_Sync_Hack_Return:
J				0x00011C50
NOP
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000
;	End of Position Sync Hack

;-------------------------------;

;	Tribal State Hack
.long				0xD2048180
LHU	A3,	0x00DC	(A1)	;Original instruction
.long				0x82048180
LHU	A3,	0x005C	(A1)	;Load saved tribal bits instead of killed tribal bits
.align				3
.long				0xE0000000
.long				0x00000000

.long				0xD20481E8
SH	T2,	0x00DC	(A1)	;Original instruction
.long				0x820481E8
SH	T2,	0x005C	(A1)	;Write to saved tribal bits instead of killed tribal bits
.align				3
.long				0xE0000000
.long				0x00000000

;	Tribal state rewarp
;	Begin C0DE segment
.long				0xC0DE0000
.long				0x00000000

LUI	S0,	%hi(lastLocation + RDRAM_START + TRAINER_OFFSET)
LHU	S0,	%lo(lastLocation + RDRAM_START + TRAINER_OFFSET)	(S0)
				;[lastLocation]
BEQZ	S0,			TSR_Exit
NOP				;Don't allow loading of tribal state if no such state
				;ever existed

LUI	S0,	%hi(warpingDone + RDRAM_START + TRAINER_OFFSET)
LW	S0,	%lo(warpingDone + RDRAM_START + TRAINER_OFFSET)	(S0)
				;[warpingDone]
LUI	S1,	0xDEAD		;
ORI	S1,	S1,	0xBEEF	;Warping done?
BNE	S0,	S1,		TSR_Exit
NOP

LUI	S0,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
LHU	S0,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(S0)
BEQZ	S0,			TSR_Check_For_Clear
ORI	S1,	R0,	0x0001	;
BEQ	S0,	S1,		TSR_Check_For_L
NOP
BEQZ	R0,			TSR_Exit
NOP

				TSR_Check_For_Clear:
LUI	S0,	0x8010		;
LH	S0,	0x5304	(S0)	;
BNEZ	S0,			TSR_Exit
NOP
ORI	S1,	R0,	0x0001	;TSR_Check_For_L
LUI	S0,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
SH	S1,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(S0)
BEQZ	R0,			TSR_Exit
NOP

				TSR_Check_For_L:
LUI	S0,	0x8010		;
LH	S0,	0x5304	(S0)	;
ORI	S1,	R0,	0x0020	;L's value
BNE	S0,	S1,		TSR_Not_L
NOP

LUI	S0,	%hi(gameActive + RDRAM_START + TRAINER_OFFSET)
LH	S0,	%lo(gameActive + RDRAM_START + TRAINER_OFFSET)	(S0)
ORI	S1,	R0,	0x0001	;
BNE	S0,	S1,		TSR_Exit
NOP

ORI	S1,	R0,	0x0002	;Load tribal data
LUI	S0,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
SH	S1,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(S0)
LHU	S1,	%lo(lastLocation + RDRAM_START + TRAINER_OFFSET)	(S0)	;
LUI	S0,	%hi(0x800A3250)	;
SH	S1,	%lo(0x800A3250)	(S0)	;
				;warpLocation = lastLocation
ORI	S1,	R0,	0x0001	;
SB	S1,	%lo(0x800A3294)	(S0)	;
				;doWarp == true
BEQZ	R0,			TSR_Exit
NOP

				TSR_Not_L:
BEQZ	S0,			TSR_Exit
NOP
ORI	S1,	R0,	0x0003	;Don't load tribal data
LUI	S0,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
SH	S1,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(S0)

				TSR_Exit:
JR	RA			;
NOP

.align				3
.long				0xE0000000
.long				0x00000000
;	End C0DE segment

;	Keep saved tribals hack
;	If 0x80048650 == (instruction that's usually there)
;	Do 0x87 code type for skipping resetting of tribal data
;	if tribalDataLoadState == 2 && region == lastRegion,
;	else if tribalDataLoadState == 2 tribalDataLoadState = 3
.long				0xD2048650
SH	R0,	0x005C	(T7)	;Original code
.long				0x87048650
.long				0x00000000
;	T8 and T9 are free
LUI	T8,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
LHU	T9,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(T8)
ORI	T8,	R0,	0x0002	;
BNE	T8,	T9,		KSTH_Normal_Return
NOP

ADDIU	SP,	SP,	-0x0008	;Stack stuff
SW	S0,	0x0000	(SP)	;""
SW	S1,	0x0004	(SP)	;""

LUI	T8,	%hi(0x800A323C)	;
LHU	T8,	%lo(0x800A323C)	(T8)	;
ORI	T9,	R0,	0x0118	;Size of room data at 0x00CB3100
MFHI	S0			;
MFLO	S1			;
MULTU	T8,	T9		;
MFLO	T8			;
MTHI	S0			;
MTLO	S1			;
LUI	T9,	%hi(0xB0CB3100)	;
ORI	T9,	T9,	%lo(0xB0CB3100)	;
ADDU	T8,	T8,	T9	;
LBU	S0,	0x0102	(T8)	;[region]
LUI	T8,	%hi(lastRegion + RDRAM_START + TRAINER_OFFSET)
LBU	T9,	%lo(lastRegion + RDRAM_START + TRAINER_OFFSET)	(T8)
				;[lastRegion]
BEQ	S0,	T9,		KSTH_Return
NOP				;region == lastRegion?
ORI	T9,	R0,	0x0003	;
SH	T9,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(T8)
				;tribalDataLoadState = 3
SH	R0,	0x005C	(T7)	;Original code

				KSTH_Return:
LW	S0,	0x0000	(SP)	;Stack stuff
LW	S1,	0x0004	(SP)	;""
ADDIU	SP,	SP,	0x0008	;""
J				0x00048654
NOP

				KSTH_Normal_Return:
J				0x00048654
SH	R0,	0x005C	(T7)	;Original code
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000

.long				0xD204866C
SH	R0,	0x00DC	(T9)	;
.long				0x8204866C
NOP				;Now cut that out!
.align				3
.long				0xE0000000
.long				0x00000000

;	Keep saved count hack
;	If 0x80048678 == (instruction that's usually there)
;	Do 0x87 code type for keeping saved tribal count if
;	tribalDataLoadState == 2
.long				0xD2048678
SB	R0,	0x0004	(T0)	;Original code
.long				0x87048678
.long				0x00000000
;	T1 and T3 are free
LUI	T1,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
LHU	T1,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(T1)
ORI	T3,	R0,	0x0002	;
BNEL	T1,	T3,		KSCH_Return
SB	R0,	0x0004	(T0)	;Original code
				KSCH_Return:
J				0x0004867C
NOP
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000

;	Keep killed count hack
;	If 0x80048684 == (instruction that's usually there)
;	Do 0x87 code type for keeping killed tribal count if
;	tribalDataLoadState == 2
.long				0xD2048684
SB	R0,	0x0005	(T1)	;Original code
.long				0x87048684
.long				0x00000000
;	T2 and T3 are free
LUI	T2,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
LHU	T2,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(T2)
ORI	T3,	R0,	0x0002	;
BNEL	T2,	T3,		KKCH_Return
SB	R0,	0x0005	(T1)	;Original code
				KKCH_Return:
J				0x00048688
NOP
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000

;	WARNING: Saving in Gem Quarry after killing the first two tribals
;	and then loading the Gem Quarry again with tribal data kept
;	caused the value for missing tribals to be left as 0xFE, not
;	0x03; this is because the missing tribal count isn't properly
;	initialized in multiplayer/co-op
;	Keep missing count hack
;	If 0x800486A0 == (instruction that's usually there)
;	Do 0x87 code type for keeping missing tribal count if
;	tribalDataLoadState == 2
.long				0xD20486A0
SB	T5,	0x0006	(T6)	;Original code
.long				0x870486A0
.long				0x00000000
;	A0 and T7 are free
LUI	A0,	%hi(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)
LHU	A0,	%lo(tribalDataLoadState + RDRAM_START + TRAINER_OFFSET)	(A0)
ORI	T7,	R0,	0x0002	;
BNEL	A0,	T7,		KMCH_Return
SB	T5,	0x0006	(T6)	;Original code
				KMCH_Return:
J				0x000486A4
NOP
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000
;	End of Tribal State Hack

;-------------------------------;

;	Game Active Check
;	MUST BE ADDED TO CHEAT LIST LAST
;	gameActive = false
;	Begin C0DE segment
.long				0xC0DE0000
.long				0x00000000

LUI	S0,	%hi(gameActive + RDRAM_START + TRAINER_OFFSET)
SH	R0,	%lo(gameActive + RDRAM_START + TRAINER_OFFSET)	(S0)
JR	RA			;
NOP

.align				3
.long				0xE0000000
.long				0x00000000
;	End C0DE segment

;	If 0x80037D20 == (instruction that's usually there)
;	Do 0x87 code type for setting gameActive = true
.long				0xD2037D20
LW	T7,	0x0068	(SP)	;Original code
.long				0x87037D20
.long				0x00000000
;	T5 and T7 are free
ORI	T5,	R0,	0x0001	;gameActive = true
LUI	T7,	%hi(gameActive + RDRAM_START + TRAINER_OFFSET)
SH	T5,	%lo(gameActive + RDRAM_START + TRAINER_OFFSET)	(T7)
LUI	T5,	%hi(0x801E6010)	;
ORI	T5,	T5,	%lo(0x801E6010)	;
SH	R0,	%lo(0x00 + TRIBAL_CACHE)	(T5)	;
				;Clear lastLocation
SH	R0,	%lo(0x02 + TRIBAL_CACHE)	(T5)	;
				;Clear lastRegion and lastPlanet
				GAT_Return:
J				0x00037D24
LW	T7,	0x0068	(SP)	;Original code
.align				3
.long				0xE0000000
.long				0x00000000
.long				0xE0000000
.long				0x00000000
;	End of Game Active Check

;-------------------------------;

;	Body Linger
;JAL to this code from 0xB1EE764C and NOP the instruction after it to use the hack
;Code is currently placed at the location defined below which is JAL'd to by 0x0C1FFF00
.equ				BODY_LINGER_OFFSET, 0x007FFC00

;Make code only be moved to new memory location if it isn't there already
.long				0xD6000000 + BODY_LINGER_OFFSET
ADDIU	SP,	SP,	0xFFE8	;First instruction;
				;will copy memory if and only if it's not already there

;From the trainer spec,
;"Writes the following ([count] * 8) bytes to [address] (dword)"
.long				0x85000000 + BODY_LINGER_OFFSET
.long				(BodyLingerEnd - BodyLingerStart)/8

				BodyLingerStart:
;Back up registers
ADDIU	SP,	SP,	0xFFE8	;
SW	S0,	0x0000	(SP)	;
SW	S1,	0x0004	(SP)	;
SW	S2,	0x0008	(SP)	;
SW	S3,	0x000C	(SP)	;
SW	T1,	0x0010	(SP)	;
SW	T2,	0x0014	(SP)	;

;Load byte to check if enemy is dead
LBU	S0,	0x0055	(T0)	;

;Return if they are
ADDIU	S0,	S0,	-0x0001	;
BNEZ	S0,			BodyLingerReturn
;NOP

;Mark enemy as dead
ORI	S1,	R0,	0x0002	;
SB	S1,	0x0055	(T0)	;

;Load pointer to block containing "group pointer" and return if it's null
LW	S0,	0x0024	(T0)	;
BEQZ	S0,			BodyLingerReturn
;NOP

;Load "group pointer" and return if it's null
LW	S1,	0x0068	(S0)	;
BEQZ	S1,			BodyLingerReturn
;NOP

;Load number of enemies remaining in this group and decrement it if it is not 0
LBU	S0,	0x0000	(S1)	;
ADDIU	S2,	S0,	-0x0001	;
BNEZL	S0,			BodyLingerDecrementGroupEnemyCount
SB	S2,	0x0000	(S1)	;
OR	S2,	S0,	R0	;
				BodyLingerDecrementGroupEnemyCount:

;Return if the group is still active
BNEZ	S2,			BodyLingerReturn
;NOP

; Return if the group is not a VIP group
LBU	S0,	0x0075	(S1)	;
BEQZ	S0,			BodyLingerReturn
;NOP

;Acquire the pointer to the data regarding the area's life force door
ORI	S3,	R0,	0x0007	;Flags indicating it's a life force door
ORI	T2,	R0,	0x0028	;Flags indicating the life force door is the active one
LUI	S0,	0x800F		;
LW	S0,	0x2CA4	(S0)	;
BEQZ	S0,			BodyLingerReturn
NOP
				BodyLingerDoorSearchLoop:
LW	S1,	0x0000	(S0)	;
BEQZ	S1,			BodyLingerReturn
NOP
LHU	S2,	0x0048	(S1)	;
BNE	S2,	S3,		BodyLingerDoorSearchLoop
ADDIU	S0,	S0,	0x0004	;
LW	S2,	0x0068	(S1)	;
LHU	T1,	0x003E	(S2)	;
AND	T1,	T1,	T2	;
BNE	T1,	T2,		BodyLingerDoorSearchLoop
;NOP

;Decrement the count of VIP groups remaining if the count is not 0
LBU	S1,	0x004A	(S2)	;
ADDIU	S0,	S1,	-0x0001	;
BNEZL	S1,			BodyLingerDecrementVIPGroupCount
SB	S0,	0x004A	(S2)	;
OR	S0,	S1,	R0	;
				BodyLingerDecrementVIPGroupCount:

;Return if there are still groups active
BNEZ	S0,			BodyLingerReturn
;NOP

;Open the door
ORI	S1,	R0,	0x0030	;
SH	S1,	0x003E	(S2)	;

				BodyLingerReturn:
;Restore registers
LW	S0,	0x0000	(SP)	;
LW	S1,	0x0004	(SP)	;
LW	S2,	0x0008	(SP)	;
LW	S3,	0x000C	(SP)	;
LW	T1,	0x0010	(SP)	;
LW	T2,	0x0014	(SP)	;
JR	RA			;
ADDIU	SP,	SP,	0x0018	;

;Calculate size of this code
.align				3
				BodyLingerEnd:

;End conditional memory copy
.long				0xE0000000
.long				0x00000000
;	End of Body Linger

;-------------------------------;

;	Carnage Persistence - Limbs
.long				0x810279CC
.long				0x00002400
;	End Carnage Persistence - Limbs

;-------------------------------;

;	Carnage Persistence - Graphical Fix for Bodies
.long				0x82015FB0
.long				0x240D0000
;	End Carnage Persistence - Graphical Fix for Bodies

;-------------------------------;
;	END OF CHEAT LIST	;
;-------------------------------;

.long				0xFFFFFFFF
.long				0xFFFFFFFF
